package mm.chenchen.modules.script.service.impl;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import mm.chenchen.common.utils.FileUtils;
import mm.chenchen.common.utils.MinioClientUtil;
import mm.chenchen.common.utils.MinioUtil;
import mm.chenchen.constant.ResultCode;
import mm.chenchen.constant.ResultMsg;
import mm.chenchen.modules.host.entity.HostInfo;
import mm.chenchen.modules.host.service.HostInfoService;
import mm.chenchen.modules.script.dao.ScriptInstanceDao;
import mm.chenchen.modules.script.dao.model.request.ScriptInstanceQuery;
import mm.chenchen.modules.script.entity.ScriptInstance;
import mm.chenchen.modules.script.entity.ScriptMirroring;
import mm.chenchen.modules.script.entity.ScriptRunHis;
import mm.chenchen.modules.script.service.ScriptInstanceService;
import mm.chenchen.modules.script.service.ScriptMirroringService;
import mm.chenchen.modules.script.service.ScriptRunHisService;
import mm.chenchen.modules.script.thread.ScriptInstanceThreadCache;
import mm.chenchen.script.ScriptUtils;
import mm.chenchen.utils.IniUtils;
import mm.chenchen.utils.PageModel;
import mm.chenchen.utils.R;
import mm.chenchen.utils.SshUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.*;
import java.util.Date;
import java.util.Map;
import java.util.UUID;


@Service("scriptInstanceService")
////@DataSource("爬虫资源库")
@Slf4j
public class ScriptInstanceServiceImpl extends ServiceImpl<ScriptInstanceDao, ScriptInstance> implements ScriptInstanceService {

    @Autowired
    private MinioClientUtil minioClientUtil;
    @Autowired
    private ScriptMirroringService service;
    @Autowired
    private ScriptRunHisService hisService;
    @Autowired
    private HostInfoService hostInfoService;

    @Value("${script.path}")
    private String basePath;
    @Value("${script.tmp-path}")
    private String tmpPath;

    public R run(ScriptInstance data, String exec, String logName, Long operId) {
        ScriptRunHis his = new ScriptRunHis();
        his.setInstanceId(data.getId());
        his.setCommand(exec);
        final String[] name = {""};
        ScriptInstanceThreadCache.SCRIPTS_THREAD_POOL.execute(() -> {
            log.info("开始执行线程：" + Thread.currentThread().getName());
            name[0] = Thread.currentThread().getName();
            ScriptInstanceThreadCache.SCRIPTS_THREAD_POOL_STATUS.put(data.getId(), ResultCode.RUNNING);
            his.setStartTime(new Date());
            int code = 0;

            //判断实例是本地还是远程
            if (-99l == data.getHostId()) {
                 code = ScriptUtils.exec(exec, logName);
            } else {
                HostInfo hostInfo = (HostInfo) hostInfoService.getById(data.getHostId()).getData();
                Connection connection=SshUtils.login(hostInfo.getIp(),hostInfo.getUser(),hostInfo.getPassword());
                SshUtils.execute(connection,exec);


            }

            his.setRunStatus(code);
            his.setEndTime(new Date());
            his.setOperId(operId);
            try {
                InputStream inputStream = new FileInputStream(logName);
                String minioId = UUID.randomUUID().toString().trim().replaceAll("-", "");
                String fileName = "script/python" + "/" + minioId + ".log";
                MinioUtil.upload(minioClientUtil.getMinioClient(), "log", inputStream, fileName);
                his.setLogAddr(fileName);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            hisService.saveInfo(his);
            ScriptInstanceThreadCache.SCRIPTS_THREAD_POOL_STATUS.put(data.getId(), code);
        });
        //ScriptPythonInstanceThread.SCRIPTS_THREAD_POOL.shutdown(); //清空连接池
        return R.ok();
    }


    public R findPage(Page<Map> page, ScriptInstanceQuery param) {
        IPage<ScriptInstance> pageData = baseMapper.findPage(page, param);
        Thread t = Thread.currentThread();
        log.info("当前运行线程为: {}", t.getName());
        if (pageData != null) {
            return R.ok().setData(new PageModel(pageData.getRecords(), pageData.getTotal(), pageData.getSize(), pageData.getCurrent()));
        } else {
            return R.error(ResultMsg.DATA_NOT_EXIT);
        }
    }

    public R getById(Long id) {
        return R.ok().setData(super.getById(id));
    }

    public R saveInfo(ScriptInstance param) throws Exception{
        param.setCreateTime(new Date());
        if (-99l == param.getHostId()) {
            localDeploy(param);
        } else {
            HostInfo hostInfo = (HostInfo) hostInfoService.getById(param.getHostId()).getData();
            if (ObjectUtils.isEmpty(hostInfo)) {
                return R.error("主机信息不存在");
            }
            if (!SshUtils.Connectable(hostInfo.getIp(), hostInfo.getUser(), hostInfo.getPassword())) {
                return R.error("主机无法连接!");
            }
            remoteDeploy(param, hostInfo);
        }
        return R.ok().setData(super.save(param));
    }

    public R getConfig(Long objId) {
        ScriptInstance data = super.getById(objId);
        return R.ok().setData(IniUtils.readIni(basePath + data.getFilePath() + "/config/properties.conf"));
        //return R.ok().setData(IniUtils.readIni("C:\\Users\\dev\\Desktop\\chenchen-fast-start\\chenchen-fast-start\\script\\python\\venv\\Include\\reptile\\qidian\\config\\properties.conf"));
    }

    @Override
    public R updateConfig(Long objId, Map param) {
        ScriptInstance data = super.getById(objId);
        return R.ok().setData(IniUtils.write(basePath + data.getFilePath() + "/config/properties.conf", param));
        //return R.ok().setData(IniUtils.write("C:\\Users\\dev\\Desktop\\chenchen-fast-start\\chenchen-fast-start\\script\\python\\venv\\Include\\reptile\\qidian\\config\\properties.conf", param));
    }

    @Override
    public R runInstance(Long objId, Long operId) {
        ScriptInstance data = super.getById(objId);
        Long currentTime = System.currentTimeMillis();
        String logName = basePath + data.getFilePath() + "/log/" + currentTime + ".log";
        String exec = "sh " + basePath + data.getFilePath() + "/start.sh " + basePath + data.getFilePath() + "  " + basePath + data.getFilePath() + "/log/" + currentTime + ".log";
        log.info("开始运行实例: {}", data.getId());
        run(data, exec, logName, operId);
        return R.ok();
    }

    public R update(ScriptInstance param) {
        ScriptInstance data = super.getById(param.getId());
        if (ObjectUtils.isEmpty(data)) {
            return R.error(ResultMsg.DATA_NOT_EXIT);
        }
        return R.ok().setData(super.updateById(param));
    }

    public R deleteById(Long id) {
        ScriptInstance param = super.getById(id);
        if (-99l == param.getHostId()) {
            log.info("=============删除实例文件==============");
            ScriptUtils.exec("rm -rf " + basePath + param.getFilePath() + "/");
        } else {
            HostInfo hostInfo = (HostInfo) hostInfoService.getById(param.getHostId()).getData();
            Connection connection=SshUtils.login(hostInfo.getIp(),hostInfo.getUser(),hostInfo.getPassword());
            String path = basePath + param.getFilePath();
            SshUtils.execute(connection,"rm -rf " + path + "/" );
        }
        return R.ok().setData(super.removeById(id));
    }

    private void localDeploy(ScriptInstance param) {
        String path = basePath + param.getFilePath();
        File filePath = new File(path);
        System.out.println("文件的保存路径：" + path);
        if (!filePath.exists() && !filePath.isDirectory()) {
            System.out.println("目录不存在，创建目录:" + filePath);
            filePath.mkdirs();
        }
        ScriptMirroring mirroring = service.getById(param.getMirroringId()).getData();
        InputStream inputStream = MinioUtil.download(minioClientUtil.getMinioClient(), mirroring.getBucket(), mirroring.getMinioId());
        try {
            FileUtils.writeFile(inputStream, basePath + param.getFilePath() + "/" + mirroring.getName() + "." + mirroring.getType());
        } catch (IOException e) {
            log.error("实例写入文件失败");
        }
        log.info("=============开始解压文件==============");
        if ("rar".equals(mirroring.getType())) {
            ScriptUtils.exec("rar x " + basePath + param.getFilePath() + "/" + mirroring.getName() + "." + mirroring.getType() + " " + basePath + param.getFilePath());
        } else {

        }
        log.info("=============删除解压文件==============");
        ScriptUtils.exec("rm -rf " + basePath + param.getFilePath() + "/" + mirroring.getName() + "." + mirroring.getType());

    }

    private void remoteDeploy(ScriptInstance param, HostInfo hostInfo) throws Exception{
        String path = basePath + param.getFilePath();
        log.info("文件的保存路径：" + path);

        String uuid = UUID.randomUUID().toString().replaceAll("-","");
        File tmpFilePath = new File(tmpPath+uuid);
        if (!tmpFilePath.exists() && !tmpFilePath.isDirectory()) {
            log.info("目录不存在，创建临时文件目录:" + tmpFilePath);
            tmpFilePath.mkdirs();
        }
        Connection connection=SshUtils.login(hostInfo.getIp(),hostInfo.getUser(),hostInfo.getPassword());
        ScriptMirroring mirroring = service.getById(param.getMirroringId()).getData();
        InputStream inputStream = MinioUtil.download(minioClientUtil.getMinioClient(), mirroring.getBucket(), mirroring.getMinioId());
        String tmpFileFullName=tmpFilePath + "/" + mirroring.getName() + "." + mirroring.getType();
        try {
            log.info("开始写入临时文件"+tmpFileFullName);
            FileUtils.writeFile(inputStream,tmpFileFullName );
        } catch (IOException e) {
            log.error("写入临时文件失败");
        }
        log.info("=============开始解压文件==============");
        if ("rar".equals(mirroring.getType())) {
            SshUtils.execute(connection,"mkdir -p "+path);
            SCPClient scpClient = connection.createSCPClient();
            log.info("开始拷贝文件  : [{}] ====> [{}]",tmpFileFullName,path);
            scpClient.put(tmpFileFullName,path);
            SshUtils.execute(connection,"rar x " + path + "/" + mirroring.getName() + "." + mirroring.getType() + " " + path);
        } else {

        }
        log.info("=============删除解压文件==============");
        SshUtils.execute(connection,"rm -rf " + path + "/" + mirroring.getName() + "." + mirroring.getType());
        ScriptUtils.exec("rm -rf " + tmpFilePath);
    }

}